package io.hepu.robotize.auth;

import io.hepu.robotize.IContext;
import io.hepu.robotize.util.Limits;
import io.hepu.robotize.util.Serializables;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class JRedis {
    public static final String STATUS_OK = "OK";

    /**
     * Get Jedis pool.
     * @return JedisPool
     */
    public static JedisPool jedisPool() {
        return IContext.me().getJedisPool();
    }

    /**
     * Get Jedis instance.
     *
     * @return redis.clients.jedis.Jedis
     */
    public static Jedis jedis() {
        JedisPool jedisPool = jedisPool();
        if (Limits.isNull(jedisPool)) {
            throw new NullPointerException("Jedis Pool is NULL");
        }
        return jedisPool.getResource();
    }

    /**
     * Release Jedis resources.
     */
    public static void shutdown() {
        JedisPool jedisPool = jedisPool();
        if (null != jedisPool) {
            jedisPool.close();
        }
    }

    /**
     * Get the string value of the specified key.
     *
     * @param key the specified key
     * @return java.lang.String
     */
    public static String string(final String key) {
        Object object = deserialize(key);
        return Limits.nonNull(object) ? object.toString() : null;
    }

    /**
     * Get the object value of the specified key.
     *
     * @param key the specified key
     * @return java.lang.Object
     */
    public static Object deserialize(final String key) {
        try (Jedis jedis = jedis()) {
            byte[] bytes = jedis.get(key.getBytes());
            return Limits.nonNull(bytes) ? Serializables.deserialize(bytes) : null;
        }
    }

    /**
     * Set the object value as value of the key.
     *
     * @param key   the specified key
     * @param value the object value
     * @return Status code reply
     */
    public static String serialize(final String key, final Object value) {
        try (Jedis jedis = jedis()) {
            return jedis.set(key.getBytes(), Serializables.serialize(value));
        }
    }

    /**
     * Set the object value as value of the key with expire time.
     *
     * @param key    the specified key
     * @param value  the object value
     * @param expire seconds
     * @return java.lang.Long
     */
    public static Long serialize(final String key, final Object value, final long expire) {
        try (Jedis jedis = jedis()) {
            String status = jedis.set(key.getBytes(), Serializables.serialize(value));
            return STATUS_OK.equals(status) ? jedis.expire(key.getBytes(), expire) : -1L;
        }
    }

    /**
     * Get the string value of the specified key.
     *
     * @param key the specified key
     * @return Bulk reply
     */
    public static String get(final String key) {
        try (Jedis jedis = jedis()) {
            return jedis.get(key);
        }
    }

    /**
     * Set the string value as value of the key.
     *
     * @param key   the specified key
     * @param value the string value
     * @return Status code reply
     */
    public static String set(final String key, final String value) {
        try (Jedis jedis = jedis()) {
            return jedis.set(key, value);
        }
    }

    /**
     * Set the string value as value of the key with seconds time.
     *
     * @param key    the specified key
     * @param value  the string value
     * @param expire the expire time as seconds
     * @return java.lang.Long
     */
    public static Long set(final String key, final String value, final long expire) {
        try (Jedis jedis = jedis()) {
            String status = jedis.set(key, value);
            return STATUS_OK.equals(status) ? jedis.expire(key, expire) : -1;
        }
    }

    /**
     * Remove the specified key.
     *
     * @param key the specified key
     * @return java.lang.Long
     */
    public static Long delete(final String key) {
        try (Jedis jedis = jedis()) {
            return jedis.del(key.getBytes());
        }
    }

    /**
     * Test if the specified key exists.
     *
     * @param key the specified key
     * @return Boolean reply, true if the key exists, otherwise false
     */
    public static Boolean exists(final String key) {
        try (Jedis jedis = jedis()) {
            return jedis.exists(key.getBytes());
        }
    }

    public static Set<String> keys(final String pattern) {
        try (Jedis jedis = jedis()) {
            return jedis.keys(pattern);
        }
    }

    /**
     * Returns all the keys matching.
     *
     * @param pattern the key pattern
     * @return Multi bulk reply
     */
    public static Set<byte[]> keys(final byte[] pattern) {
        try (Jedis jedis = jedis()) {
            return jedis.keys(pattern);
        }
    }

    /**
     * Returns the remaining time to live in seconds of a key.
     *
     * @param key the specified key
     * @return java.lang.Long
     */
    public static Long ttl(final String key) {
        try (Jedis jedis = jedis()) {
            return jedis.ttl(key);
        }
    }

    /**
     * Set a timeout on the specified key.
     *
     * @param key    the specified key
     * @param expire the expire time
     */
    public static Long expire(String key, long expire) {
        try (Jedis jedis = jedis()) {
            return jedis.expire(key.getBytes(), expire);
        }
    }

    public static Long expireAt(String key, long expireAt) {
        try (Jedis jedis = jedis()) {
            return jedis.expireAt(key.getBytes(), expireAt);
        }
    }

    /**
     * Increment integer and store as string.
     *
     * @param key   the specified key
     * @param delta the increment
     * @return java.lang.Long
     */
    public static Long increment(String key, long delta) {
        try (Jedis jedis = jedis()) {
            return jedis.incrBy(key, delta);
        }
    }

    public static Long incr(String key) {
        try (Jedis jedis = jedis()) {
            return jedis.incr(key);
        }
    }

    /**
     * Decrement integer and store as string.
     *
     * @param key   the specified key
     * @param delta the decrement
     * @return java.lang.Long
     */
    public static Long decrement(String key, long delta) {
        try (Jedis jedis = jedis()) {
            return jedis.decrBy(key, delta);
        }
    }

    public static Long decr(String key) {
        try (Jedis jedis = jedis()) {
            return jedis.decr(key);
        }
    }

    /**
     * Retrieve the value associated to the specified field.
     *
     * @param hkey the hash key
     * @param key  the specified key
     */
    public static String hget(String hkey, String key) {
        try (Jedis jedis = jedis()) {
            return jedis.hget(hkey, key);
        }
    }

    /**
     * Set the specified hash field to the specified value.
     *
     * @param hkey  the hash key
     * @param key   the specified key
     * @param value the string value
     */
    public static long hset(String hkey, String key, String value) {
        try (Jedis jedis = jedis()) {
            return jedis.hset(hkey, key, value);
        }
    }

    // Set the specified hash field to the specified value if the field not exists.
    public static long hsetnx(String hkey, String key, String value) {
        try (Jedis jedis = jedis()) {
            return jedis.hsetnx(hkey, key, value);
        }
    }

    // Remove the specified field from a hash stored at key.
    public static long hdel(String hkey, String... key) {
        try (Jedis jedis = jedis()) {
            return jedis.hdel(hkey, key);
        }
    }

    // Test for existence of a specified field in a hash.
    public static Boolean hexists(String hkey, String key) {
        try (Jedis jedis = jedis()) {
            return jedis.hexists(hkey, key);
        }
    }

    // Return all the fields in a hash.
    public static Set<String> hkeys(String hkey) {
        try (Jedis jedis = jedis()) {
            return jedis.hkeys(hkey);
        }
    }

    // Return all the values in a hash.
    public static List<String> hvals(String hkey) {
        try (Jedis jedis = jedis()) {
            return jedis.hvals(hkey);
        }
    }

    // Return all the fields and associated values in a hash.
    public static Map<String, String> hgetAll(String hkey) {
        try (Jedis jedis = jedis()) {
            return jedis.hgetAll(hkey);
        }
    }

    // Delete all the keys of the currently selected DB.
    public static String flushDB() {
        try (Jedis jedis = jedis()) {
            return jedis.flushDB();
        }
    }

    // Delete all the keys of all the existing databases, not just the currently selected one.
    public static String flushAll() {
        try (Jedis jedis = jedis()) {
            return jedis.flushAll();
        }
    }

    // Generate long id value based on cache key
    public static long longId(String cacheKey) {
        if (!exists(cacheKey)) {
            throw new NullPointerException("Redis key is not exists");
        }
        return incr(cacheKey);
    }

    public static String stringId(String cacheKey) {
        return String.valueOf(longId(cacheKey));
    }
}
